package com.nutquest.client;

import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import java.util.*;
import javax.swing.*;
import com.nutquest.common.dataobjects.Player;
import com.nutquest.common.dataobjects.Terrain;
import com.nutquest.common.mapdata.IMapData;
import com.nutquest.common.mapdata.MapOne;
import com.nutquest.server.GameServer;
import com.nutquest.server.Ports;
import com.nutquest.client.ForagingMap;

/**
 * The Class NutQuestInterface.
 */
public class NutQuestClient implements ActionListener, Ports {

	/** The cards. */
	JPanel cards; // a panel that uses CardLayout

	/** The Constant LOGINPANEL. */
	final static String LOGINPANEL = "login";

	/** The Constant LOBBYPANEL. */
	final static String LOBBYPANEL = "lobby";

	/** The Constant GAMEPLAYPANEL. */
	final static String GAMEPLAYPANEL = "game";

	/** The user name. */
	// static String userName;

	/** The player. */
	static Player player;

	/** The start game btn. */
	static JButton connectBtn, logoutBtn, lobbySendBtn, startGameBtn;

	/** The game play card. */
	static JPanel loginCard, lobbyCard, lobbySouthPanel, gamePlayCard;

	/** The lobby chat users. */
	static JTextArea lobbyChatTArea, lobbyChatUsers;

	/** The lobby t field. */
	static JTextField lobbyTField;

	/** The chat pane. */
	static JScrollPane lobbyChatScrollPane, chatPane;

	/** The ip add. */
	static JTextField userLogin, ipAdd;

	/** The s. */
	static Socket s;

	/** The host. */
	static String host = null;
	// private RunMe runme;

	/** The enter. */
	static boolean connected, enter;

	/** The logout. */
	static boolean logout;

	/** The c socket. */
	static Socket cSocket;

	/** The out. */
	static ObjectOutputStream out;

	static JFrame frame;

	/** The in. */
	static ObjectInputStream inStream;

	/** The uinput. */
	static userInput uinput;

	/** The sinput. */
	static readFromServer sinput;

	/** The list. */
	static DefaultListModel list = new DefaultListModel();;

	/** The nick list. */
	static JList nickList = new JList(list);

	static Vector<Player> players = new Vector<Player>();

	static MapOne mapData; // card looks

	/**
	 * Adds the component to pane.
	 * 
	 * @param pane
	 *            the pane
	 */
	public void addComponentToPane(Container pane) {

		JPanel nutQuestInterface = new JPanel(); // use FlowLayout

		// --------------------CREATE LOGIN CARD----------------------------
		loginCard = new JPanel();

		// create and add user name and password buttons and text fields
		loginCard.add(new JLabel("Username:"));
		userLogin = new JTextField(10);
		loginCard.add(userLogin);
		loginCard.add(new JLabel("IP address of the server:"));
		ipAdd = new JTextField(10);
		ipAdd.setText(host);
		loginCard.add(ipAdd);

		// create the connect button and add its action listener
		connectBtn = new JButton("Connect");
		connectBtn.addActionListener(this);

		// add the connect button to the card
		loginCard.add(connectBtn);

		// --------------------CREATE LOBBY CARD----------------------------
		lobbyCard = new JPanel();
		lobbyCard.setLayout(new BorderLayout());

		// create lobbyCard sub panels
		lobbySouthPanel = new JPanel();
		lobbySouthPanel.setLayout(new BorderLayout());

		// create and add lobby's chat text area with its scroll pane

		// and chat window panel

		JPanel loobyCenter = new JPanel(new BorderLayout());
		lobbyChatTArea = new JTextArea();
		lobbyChatScrollPane = new JScrollPane(lobbyChatTArea);
		lobbyChatTArea.setEditable(false);

		lobbyChatUsers = new JTextArea(10, 10);
		lobbyChatUsers.setEditable(false);

		chatPane = new JScrollPane(lobbyChatUsers);
		chatPane.setViewportView(nickList);
		// lobbyChatUsers.setText(""+nickList);

		loobyCenter.add(lobbyChatScrollPane, BorderLayout.CENTER);
		// loobyCenter.add(lobbyChatUsers, BorderLayout.EAST);
		loobyCenter.add(chatPane, BorderLayout.EAST);

		// lobbyChatTArea.add(lobbyChatScrollPane);

		// create the back to login screen button and add its action listener
		logoutBtn = new JButton("Disconnect");
		logoutBtn.addActionListener(this);

		// create the send button for chat channel and add its action listener
		lobbySendBtn = new JButton("Send");
		lobbySendBtn.addActionListener(this);

		// create the start game Button and add its action listener
		startGameBtn = new JButton("Start Game");

		// create the lobby text field which contains user text messages to send
		lobbyTField = new JTextField(40);
		lobbyTField.setToolTipText("Enter message here ");

		lobbyTField.addKeyListener(new java.awt.event.KeyAdapter() {
			public void keyReleased(java.awt.event.KeyEvent evt) {
				inputTextKeyReleased(evt);
			}
		});

		lobbyTField.addKeyListener(new java.awt.event.KeyAdapter() {
			public void keyReleased(java.awt.event.KeyEvent evt) {
				inputTextKeyReleased(evt);
			}
		});

		// add the chat text field to the Southern lobby panel on the left hand
		lobbySouthPanel.add(lobbyTField, BorderLayout.WEST);

		// create souther lobby sub panel for buttons
		JPanel lobbySouthSubPanel = new JPanel();
		// add the disconnect,start game and logout buttons to the Southern
		// lobby panel
		lobbySouthSubPanel.add(lobbySendBtn);
		lobbySouthSubPanel.add(startGameBtn);
		lobbySouthSubPanel.add(logoutBtn);

		// place the southern lobby sub panel for buttons on the right hand of
		// the southern lobby panel
		lobbySouthPanel.add(lobbySouthSubPanel, BorderLayout.EAST);

		// add lobby components to the lobby card panel
		// lobbyCard.add(lobbyChatTArea, BorderLayout.CENTER);
		lobbyCard.add(loobyCenter, BorderLayout.CENTER);
		lobbyCard.add(lobbySouthPanel, BorderLayout.SOUTH);

		// --------------------CREATE GAMEPLAY CARD----------------------------
		// gamePlayCard = new JPanel();
		// gamePlayCard.setLayout(new BorderLayout());

		// // player data
		// players.add(new Player("1", 0, 0));
		// players.add(new Player("2", 0, 1));
		// players.add(new Player("3", 2, 0));
		//

		startGameBtn.addActionListener(this);

		// same for list of usernames

		// gamePlayCard.add(chatPane, BorderLayout.EAST);

		// TODO GAME PLAY

		// Create the panel that contains the "cards".
		cards = new JPanel(new CardLayout());
		cards.add(loginCard, LOGINPANEL);
		cards.add(lobbyCard, LOBBYPANEL);

		pane.add(nutQuestInterface, BorderLayout.PAGE_START);
		pane.add(cards, BorderLayout.CENTER);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent ae) {

		CardLayout cl = (CardLayout) (cards.getLayout());

		if (ae.getSource() == connectBtn) {

			if (userLogin.getText().equals("")) {
				JOptionPane.showMessageDialog(null, "Enter Username",
						"Enter Username", JOptionPane.INFORMATION_MESSAGE);
			}
			/*
			 * else if (ipAdd.getText().equals("") || ipAdd.getText().matches(
			 * "^.[0-9]{1,2}/..[0-9]{1,2}/..[0-9]{1,2}/..[0-9]{1,2}" ) == false
			 * ) { JOptionPane.showMessageDialog(null,
			 * "IP Address is an invalid format!  Must be: [0-255].[0-255].[0-255].[0-255]"
			 * , "Enter IP address of the server",
			 * JOptionPane.INFORMATION_MESSAGE); }
			 */

			else if (checkIp(ipAdd.getText()) == false) {
				JOptionPane
						.showMessageDialog(
								null,
								"IP Address is an invalid format!  Must be: [0-255].[0-255].[0-255].[0-255]",
								"Enter IP address of the server",
								JOptionPane.INFORMATION_MESSAGE);
			} else {
				// TODO take username for the chat

				logout = false;

				sinput = new readFromServer(this);

				uinput = new userInput();

				cSocket = null;
				out = null;
				// in = null;

				boolean error;
				error = false;

				try {

					host = ipAdd.getText(); // get the IP Address of the server
					// for
					// set the hosts username
					player = new Player(userLogin.getText().trim(), -1, -1);

					cSocket = new Socket(host, PORT);

					out = new ObjectOutputStream(cSocket.getOutputStream());
					inStream = new ObjectInputStream(cSocket.getInputStream());

					uinput.start();
					sinput.start();
				} catch (UnknownHostException e) {

					JOptionPane.showMessageDialog(null, "There is no server",
							"ERROR", JOptionPane.ERROR_MESSAGE);
					System.out.println("Host Error" + e);

					error = true;
				}

				catch (NumberFormatException n1) {

					JOptionPane
							.showMessageDialog(
									null,
									"IP Address is an invalid format!  Must be: [0-255].[0-255].[0-255].[0-255]",
									"Enter IP address of the server",
									JOptionPane.INFORMATION_MESSAGE);

				} catch (IOException e) {
					System.out.println("IOException" + e);
				}
				if (!error) {

					send("Login: " + player.getName());

				}

				cl.show(cards, LOBBYPANEL);

				/*
				 * host = ipAdd.getText(); // get the IP Address of the server
				 * for // connection; userName = userLogin.getText(); // set the
				 * hosts username
				 * 
				 * try { // create a socket for connection and a thread s = new
				 * Socket(host, PORT); // Connect to server
				 * System.out.println("You are connected to " + host);
				 * 
				 * player = new Player(userName, 0, 0); // set the player object
				 * 
				 * try {
				 * 
				 * ObjectOutputStream out = new ObjectOutputStream(
				 * s.getOutputStream());
				 * 
				 * out.writeObject(player); out.flush();
				 * 
				 * } catch (IOException e) { // TODO Auto-generated catch block
				 * e.printStackTrace(); }
				 * 
				 * runme = new RunMe(s);
				 * 
				 * Thread thread = new Thread(runme); // create a new thread //
				 * for the host thread.start(); // start the host thread
				 * 
				 * cl.show(cards, LOBBYPANEL);
				 * 
				 * } catch (Exception no) { JOptionPane.showMessageDialog(null,
				 * "The server is not running"); }
				 */

			}

		} else if (ae.getSource() == logoutBtn) {
			cl.show(cards, LOGINPANEL);

			send("Logout");
			logout = true;
			System.exit(0);

		} else if (ae.getSource() == startGameBtn) {
			sendStartingGame();
			cl.show(cards, GAMEPLAYPANEL);
			gamePlayCard.requestFocusInWindow();
		}

		if (ae.getSource() == lobbySendBtn) {
			// here we are sending chat to chatServer and receiving back data
			// and punting it in the windows

			// send message
			sendInput();

			// player.setSentMsg(lobbyTField.getText());

			// runme.send(player); // get the message and send it to
			// server

			// lobbyChatTArea.append(lobbyTField.getText() + "\n");

		}
	}

	/**
	 * Create the GUI and show it. For thread safety, this method should be
	 * invoked from the event dispatch thread.
	 */

	private void createAndShowGUI() {
		// Create and set up the window.
		frame = new JFrame("Nut Quest");

		// add key listener
		// frame.addKeyListener(new GuiKeyListener());

		// Manually set focus to this component so that it can get key events.
		// frame.setFocusable(true);
		// frame.requestFocusInWindow();
		// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		// Create and set up the content pane.
		NutQuestClient login = new NutQuestClient();
		login.addComponentToPane(frame.getContentPane());

		// take input from keyboard to play a game
		/*
		 * frame.addKeyListener(new java.awt.event.KeyAdapter() { public void
		 * keyPressed(KeyEvent ePress) { PlayMeKeys(ePress); // method for
		 * controlling keys } });
		 */

		nickList.addMouseListener(new java.awt.event.MouseAdapter() {
			public void mouseClicked(java.awt.event.MouseEvent evt) {
				nickListMouseClicked(evt);
			}
		});

		// TODO EXIT AND SEND CLIENT EXIT
		frame.addWindowListener(new WindowAdapter() {

			@Override
			public void windowClosing(WindowEvent e) {

				try {

					send("Logout");
					connected = false;
					logout = true;

					System.out.println("Window Closing " + player.getName()
							+ "has lef the game");
					System.exit(0);
				} catch (NullPointerException n) {

					System.exit(0);

				}
			}

		});

		list.addElement("Not Connected");

		// nickList = new JList(list);
		// chatPane.setViewportView(nickList);

		enter = false;
		connected = false;
		// Display the window.
		frame.setSize(800, 600);
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
	}

	/**
	 * Check ip address.
	 * 
	 * @param ip
	 *            the ip address
	 * @return true, if successful check
	 */
	public static boolean checkIp(String ip) {
		String[] parts;
		int i = 0;

		if (ip.length() == 0) {
			ip = "127.0.0.1";
		}

		parts = ip.split("\\.");

		for (String s : parts) {

			try {
				i = Integer.parseInt(s);
			} catch (NumberFormatException d) {
				JOptionPane
						.showMessageDialog(
								null,
								"IP Address is an invalid format!  Must be: [0-255].[0-255].[0-255].[0-255]",
								"Enter IP address of the server",
								JOptionPane.INFORMATION_MESSAGE);
			}

			if (i < 0 || i > 255) {
				return false;
			}
		}
		return true;
	}

	// the other methods

	// Send Message
	/**
	 * Send.
	 * 
	 * @param msg
	 *            the message from the client
	 */
	static void send(String msg) {
		try {
			out.reset();
			out.writeObject(msg);
			out.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	static void sendStartingGame() {
		try {
			out.reset();
			out.writeObject("StartingGame");
			out.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Read message.
	 * 
	 * @return the message
	 */
	static String readString() {
		// read the message
		String s = null;
		try {

			s = (String) inStream.readObject();
		} catch (Exception e) {
			System.out.println(e);
		}

		return s;
	}

	/**
	 * Method will wait for a terrain object to be sent and then update the map.
	 */
	static void readTerrain() {
		try {
			Terrain terr = (Terrain) inStream.readObject();
			ForagingMap foragingMap = (ForagingMap) gamePlayCard;
			foragingMap.updateTerrain(terr);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	static void readPlayers() {
		try {
			players.clear();
			players = (Vector<Player>) inStream.readObject();

			ForagingMap foragingMap = (ForagingMap) gamePlayCard;
			foragingMap.updatePlayers(players);
			// gamePlayCard.requestFocusInWindow();
			foragingMap.setFocusable(true);
			// foragingMap.requestFocusInWindow();

			for (Player thisPlayer : players) {
				if (thisPlayer.getName()
						.equals(NutQuestClient.player.getName())) {
					NutQuestClient.player = thisPlayer;
				}
			}
		} catch (Exception e) {
			System.out.println(e);
		}
	}

	public void readMap() {
		IMapData mapData;
		try {
			mapData = (IMapData) inStream.readObject();
			gamePlayCard = new ForagingMap(mapData, players);
			gamePlayCard.setLayout(new BorderLayout());
			gamePlayCard.setFocusable(true);

			// Add Key listener for navigational input.
			gamePlayCard.addKeyListener( new GuiKeyListener());
			cards.add(gamePlayCard, GAMEPLAYPANEL);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	/**
	 * Replace.
	 * 
	 * @param str
	 *            the message
	 * @param pattern
	 *            the pattern
	 * @param replace
	 *            the replace
	 * @return the message
	 */
	static String replace(String str, String pattern, String replace) {
		int s = 0;
		int e = 0;
		StringBuffer result = new StringBuffer();
		while ((e = str.indexOf(pattern, s)) >= 0) {
			result.append(str.substring(s, e));
			result.append(replace);
			s = e + pattern.length();
		}
		result.append(str.substring(s));
		return result.toString();
	}

	// send msg
	/**
	 * Send input.
	 */
	public void sendInput() {

		if (!connected) {
			JOptionPane.showMessageDialog(null, "Not connected!", "Error",
					JOptionPane.ERROR_MESSAGE);
			lobbyTField.setText("");
		}

		else if (lobbyTField.getText().equals("")
				|| lobbyTField.getText().equals("\n")
				|| lobbyTField.getText() == null) {
			lobbyTField.setText("");
		} else {

			send("Post " + replace(lobbyTField.getText(), "\n", " "));
			lobbyTField.setText("");
		}
	}

	/**
	 * Nick list mouse clicked.
	 * 
	 * @param evt
	 *            the event
	 */
	private static void nickListMouseClicked(java.awt.event.MouseEvent evt) {
		// get nickname
		if (connected
				&& (!nickList.getSelectedValue().equals(player.getName()))) {
			// Send private message
			String pmsg = JOptionPane.showInputDialog(null,
					"Send a private message: ");
			if (pmsg != null) {
				// Send private message

				send("PrivatePost " + pmsg + ", " + nickList.getSelectedValue());
				pmsg = null;
				// connected = false;
			}

			System.out.println(nickList.getSelectedValue());
		}
	}

	/**
	 * Input text key released.
	 * 
	 * @param evt
	 *            the event
	 */
	private void inputTextKeyReleased(java.awt.event.KeyEvent evt) {

		if (evt.getKeyCode() == 10) {
			if (enter) {

				sendInput();
				enter = false;
			} else {

				enter = true;
			}
		}
	}

	// -----------------------------------------------------------
	// KEY LISTENER
	// -----------------------------------------------------------

	private class GuiKeyListener extends KeyAdapter {

		@Override
		public void keyPressed(KeyEvent e) {
			int keyCode = e.getKeyCode();
			String keyString = null;
			switch (keyCode) {
			case KeyEvent.VK_LEFT:
				keyString = "left";
				break;
			case KeyEvent.VK_RIGHT:
				keyString = "right";
				break;
			case KeyEvent.VK_DOWN:
				keyString = "down";
				break;
			case KeyEvent.VK_UP:
				keyString = "up";
				break;

			}

			// send to the server to request a new position in the world.
			if (keyString != null) {
				send(keyString);
			}

			System.out.println("key pressed: " + keyString);

		}
	}

	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 */
	public static void main(String[] args) {
		// create default ip address to run client with
		host = "127.0.0.1";
		if (args.length > 0) {
			host = args[0];
		}

		// Schedule a job for the event dispatch thread:
		// creating and showing this application's GUI.

		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				NutQuestClient nq = new NutQuestClient();
				nq.createAndShowGUI();
			}
		});

	}

}
